/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "root.h"
#include "directx/dxinputsystem.h"
#include "directx/dxrendersystem.h"
#include "renderwindow.h"
#include "particlesystem.h"
#include "physicsystem.h"

#include <stdlib.h>

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	/**********************************************************************************************************/
	Root::Root()
		:m_zipFactory(new ZipFactory)
		,m_fileSystemFactory(new FileSystemFactory)
		,m_threadPool(0,16)
		,m_stop(false)
		,m_pause(false)
		,m_startGame(false)
		,m_lmBut(false)
		,m_camPos(0,300,-500)
		,m_camUp(0,1,0)
		,m_camDir(0,0,0)
		,m_camMove(0)
		,m_camStrafe(0)
	{
		ArchiveManager::GetSingleton().AddArchiveFactory(m_zipFactory);
		ArchiveManager::GetSingleton().AddArchiveFactory(m_fileSystemFactory);
		ArchiveManager::GetSingleton().AddPath("../data","FileSystem");

		m_graphics.FrameStartSignal.Connect<ImmediateExecute>(this,&Root::OnFrameStart);
		m_graphics.FrameEndSignal.Connect<ImmediateExecute>(this,&Root::OnFrameEnd);


		RenderSystemPtr renderSystem = m_graphics.CreateRenderSystem("DirectXRenderSystem");
		renderSystem->StartResetSignal.Connect<ImmediateExecute>(this,&Root::OnStartReset);
		renderSystem->EndResetSignal.Connect<ImmediateExecute>(this,&Root::OnEndReset);

		PresentParams pp1(false,640,480,32,32);

		m_window = renderSystem->CreateRenderWindow("Window1", pp1);
		m_window->SetBackGroundColor(Color::Blue);

		RefPtr<InputSystemFactory> factory( new DXInputSystemFactory());

		m_input = RefPtr<InputSystem,WeakPtrExtension>(factory->CreateInstance(""));

		m_input->Init(m_window->GetID());

		m_sceneManager = m_graphics.CreateSceneManager("Octree","SM1");

		m_physicSystem = m_graphics.CreatePhysicSystem("OdePhysic","PS1");

		m_sceneManager->AddPhysicSystem(m_physicSystem);

		m_sceneManager->RenderSignal.Connect<ImmediateExecute>(this,&Root::OnRender);

		m_camera = m_sceneManager->CreateCamera("Cam1");

		m_window->SetCamera(m_camera);

		DXRenderSystemPtr dxsystem;
		WeakUtils::UnsafeCopy(dxsystem, m_graphics.GetRenderSystem());

		m_gui.Init(dxsystem->GetDirect3DGlobals()->d3dDevice.GetPointer(),m_window,m_input);

		m_gui.QuitSignal.Connect<ImmediateExecute>(this,&Root::OnQuit);

		m_gui.StartGameSignal.Connect<ImmediateExecute>(this,&Root::OnGameStart);

		m_gui.ExitGameSignal.Connect<ImmediateExecute>(this,&Root::OnExitGame);

		m_gui.ResumeGameSignal.Connect<ImmediateExecute>(this,&Root::OnResumeGame);

		m_input->MouseMoveSignal.Connect<ImmediateExecute>(this,&Root::OnMouseMove);
		m_input->MouseButtonDownSignal.Connect<ImmediateExecute>(this,&Root::OnMouseDown);
		m_input->MouseButtonUpSignal.Connect<ImmediateExecute>(this,&Root::OnMouseUp);
		m_input->MouseWheelChangeSignal.Connect<ImmediateExecute>(this,&Root::OnMouseWheel);
		m_input->KeyDownSignal.Connect<ImmediateExecute>(this,&Root::OnKeyDown);
		m_input->KeyUpSignal.Connect<ImmediateExecute>(this,&Root::OnKeyUp);
		m_input->CharPressedSignal.Connect<ImmediateExecute>(this,&Root::OnCharPressed);

	}
	/**************************************************************************************************************/
	Root::~Root()
	{
		m_log.WriteToFile("log.txt");
	}
	/**************************************************************************************************************/
	void Root::Run()
	{
		while(!m_stop)
		{
			m_input->Update();
			m_graphics.RenderOneFrame();
		}
	}
	/**************************************************************************************************************/
	void Root::OnFrameStart(unsigned long timeSinceLastFrame)
	{
		if(m_startGame && !m_pause)
		{
			m_camera->Move(m_camMove);
			m_camera->Strafe(m_camStrafe);
		}
	}
	/**********************************************************************************************************/
	void Root::OnFrameEnd(unsigned long /*timeSinceFrameStart*/)
	{
		if(m_startGame && !m_pause)
		{
		}
	}
	/**********************************************************************************************************/
	void Root::OnRender()
	{
		m_gui.Render();
	}
	/**********************************************************************************************************/
	void Root::OnStartReset()
	{
		m_gui.StartReset();
	}
	/**********************************************************************************************************/
	void Root::OnEndReset()
	{
		m_gui.FinishReset();
	}
	/**********************************************************************************************************/
	void Root::OnMouseMove(REAL x, REAL y)
	{
		if(m_lmBut)
		{
			m_camera->RotateX(y * 0.1);
			m_camera->RotateY(x * 0.1);
		}

		m_gui.MouseMove(x,y);
	}
	/**********************************************************************************************************/
	void Root::OnMouseDown(InputSystem::MouseButton but)
	{
		if(m_startGame && !m_pause && but == InputSystem::LeftButton)
			m_lmBut = true;

		m_gui.MouseDown(but);
	}
	/**********************************************************************************************************/
	void Root::OnMouseUp(InputSystem::MouseButton but)
	{
		if(m_startGame && !m_pause && but == InputSystem::LeftButton)
			m_lmBut = false;

		m_gui.MouseUp(but);
	}
	/**********************************************************************************************************/
	void Root::OnMouseWheel(REAL val)
	{
		m_gui.MouseWheel(val);
	}
	/**********************************************************************************************************/
	void Root::OnKeyDown(unsigned int key)
	{
		if(m_startGame && !m_pause)
		{
			if(key == DIK_W)
				m_camMove = -1;
			else if(key == DIK_S)
				m_camMove = 1;
			else if(key == DIK_A)
				m_camStrafe = -1;
			else if(key == DIK_D)
				m_camStrafe = 1;
			else if(key == DIK_X)
				m_camera->EnableDebug(false);
			else if(key == DIK_Z)
				m_camera->EnableDebug(true);
		}

		if(key == DIK_ESCAPE && m_startGame && !m_pause)
		{
			m_pause = true;
			m_physicSystem->SetStepSize(0);
			SetParticlesStep(0);

			m_gui.ShowGameMenu();
		}
		else if(key == DIK_ESCAPE && m_startGame && m_pause)
		{
			m_gui.ExitGame();
		}
		else if(key == DIK_ESCAPE && !m_startGame)
		{
			m_gui.ExitGame();
		}
		else
		{
			m_gui.KeyDown(key);
		}
	}
	/**********************************************************************************************************/
	void Root::OnKeyUp(unsigned int key)
	{
		if(m_startGame && !m_pause)
		{
			if(key == DIK_W)
				m_camMove = 0.0;
			else if(key == DIK_S)
				m_camMove = 0.0;
			else if(key == DIK_A)
				m_camStrafe = 0.0;
			else if(key == DIK_D)
				m_camStrafe = 0.0;
		}
		m_gui.KeyUp(key);
	}
	/**********************************************************************************************************/
	void Root::OnCharPressed(unsigned short key)
	{
		m_gui.CharPressed(key);
	}
	/**********************************************************************************************************/
	void Root::OnQuit()
	{
		m_stop = true;
	}
	/**********************************************************************************************************/
	void Root::OnResumeGame()
	{
		m_pause = false;
		m_physicSystem->SetStepSize(0.0167);
		SetParticlesStep(0.02);
	}
	/**********************************************************************************************************/
	void Root::OnExitGame()
	{
		//destroy resources
		m_startGame = false;

		m_physicSystem->DeleteAll();

		m_sceneManager->GetRootSceneNode()->DeleteAllChilds();

		m_sceneManager->DeleteAllLights();

		m_sceneManager->DeleteAllEntities();

		m_sceneManager->DeleteAllParticleSystems();

		m_sceneManager->ResetScene();
	}
	/**********************************************************************************************************/
	void Root::OnGameStart()
	{
		//load resources
		m_startGame = true;
		m_pause = false;

		m_camera->SetPerspective(45,1.0,1,10000);
		m_camera->LookAt(m_camPos,m_camDir,m_camUp);

		m_sceneManager->SetAmbientLight(Color(0.5, 0.5, 0.5));

		m_physicSystem->SetGravity(0, -19.8, 0);
		m_physicSystem->SetStepSize(0.0167);

		LightPtr light = m_sceneManager->CreateLight("Light1");
		light->SetType(Light::LT_POINT);

		SceneNodePtr lightNode = m_sceneManager->GetRootSceneNode()->CreateChild("ChildNode1");

		lightNode->Move(300,300,300);

		lightNode->AttachObject(light);

		RefPtr<PhysicMaterial, WeakPtrExtension> wood(new PhysicMaterial);
		wood->density = 0.2;
		wood->hardness = 0.8;
		wood->bounciness = 0.15;
		wood->friction = 1.0;

		int OBJS_NUM = 200;
		m_ps.clear();
		for(int i = 0; i < OBJS_NUM; ++i)
		{
			std::stringstream buf;
			buf << i;
			EntityPtr entity;
			SolidPtr body = m_physicSystem->CreateSolid("sol" + buf.str());

			REAL mMin = -2000;
			REAL mMax = 2000;
			REAL x = static_cast<REAL>(mMin + rand() * (mMax - mMin) / RAND_MAX);
			REAL y = static_cast<REAL>(mMin + rand() * (mMax - mMin) / RAND_MAX)/1000;
			REAL z = static_cast<REAL>(mMin + rand() * (mMax - mMin) / RAND_MAX);

			body->SetTransform(Matrix4<REAL>::GetTrans(x,y,z));

			entity = m_sceneManager->CreateEntity("testmesh" + buf.str(), "test.mesh");

			MeshShapeData md;
			md.entity = entity;
			md.material = wood;
			md.collisionDepth = 4.0;
			md.CollisionSignal->Connect<ImmediateExecute>(this, &Root::OnCollision);

			body->AddShape(md);


			SceneNodePtr psNode = m_sceneManager->GetRootSceneNode()->CreateChild("PsNode" + buf.str());
			psNode->SetTransform(Matrix4<REAL>::GetTrans(x,y,z));

			//particles
			ParticleSystemPtr ps = m_sceneManager->CreateParticleSystem("PS" + buf.str(), "ps1.material", 25);

			ps->SetReleaseInterval( 0.5 );
			ps->SetLifeCycle( 10.0 );
			ps->SetVelocityVar( 35.0 );
			ps->SetGravity(Vector3<REAL>(0,-45,0) );
			Plane pl(0,1,0,300);
			ps->AddCollisionPlane(pl,0.3);
			ps->SetOneShootMode(true);

			psNode->AttachObject(ps);
			m_ps[entity->GetName()] = std::make_pair(psNode,ps);
		}
		SetParticlesStep(0.02);


		Matrix4<REAL> m = Matrix4<REAL>::GetScale(10000,1,10000) *  Matrix4<REAL>::GetTrans(0,-300,0);

		m_sceneManager->AddStaticGeometry("plane.mesh",m);
		m_sceneManager->BuildStaticGeometry();

		SolidPtr body3 = m_physicSystem->CreateSolid("Floor");
		body3->SetTransform(Matrix4<REAL>::GetTrans(0,-300,0));

		BoxShapeData bd3;
		bd3.size = Vector3<REAL>(10000,1,10000);
		bd3.material = wood;

		body3->AddShape(bd3);
		body3->SetStatic(true);

	}
	/**********************************************************************************************************/
	void Root::OnCollision(EntityPtr entity, Vector3<REAL> pos, Vector3<REAL> normal, REAL depth)
	{
		if(!entity.IsNull())
		{
			SceneNodePtr node = m_ps[entity->GetName()].first;
			ParticleSystemPtr ps = m_ps[entity->GetName()].second;
			if(!ps.IsNull())
			{
				if(pos.y < -300)
					pos.y = -290;
				node->SetTransform(Matrix4<REAL>::GetTrans(pos.x,0,pos.z));
				ps->SetStartPoint(Vector3<REAL>(0,pos.y,0));
				ps->SetNumToRelease( 15 );
				ps->SetVelocity( normal * -55 );
				ps->RestartParticleSystem();
			}
		}
	}
	/**********************************************************************************************************/
	void Root::SetParticlesStep(REAL val)
	{
		std::map<std::string, std::pair<SceneNodePtr, ParticleSystemPtr> >::iterator i = m_ps.begin(), e = m_ps.end();
		for(;i != e; ++i)
		{
			i->second.second->SetStepSize(val);
		}
	}
	/**********************************************************************************************************/
}
